package com.aeoj.clientmanagement.websocket;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.aeoj.clientmanagement.domain.pk.BattleRecord;
import com.aeoj.clientmanagement.domain.problem.Problem;
import com.aeoj.clientmanagement.domain.user.SysUser;
import com.aeoj.clientmanagement.domain.userscore.UserScore;
import com.aeoj.clientmanagement.domain.userscore.vo.UserScoreVO;
import com.aeoj.clientmanagement.service.*;
import com.aeoj.clientmanagement.utils.common.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import jakarta.annotation.Resource;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

@Component
@ServerEndpoint(value = "/client/pk/{token}")  // 这个路径是建立连接时的路径
@Slf4j
public class WebsocketServer {
    // 用一个static的map集合存所有用户端建立的websocket连接
    // ConcurrentHashMap是一个线程安全的map集合
    private static final Map<Long, Session> sessions = new ConcurrentHashMap<>();
    // 每个实例都会使用的，用来查找用户
    private static SysUserService sysUserService;
    private static UserScoreService userScoreService;
    private static ProblemService problemService;
    private static BattleRecordService battleRecordService;
    // 保存当前实例的用户
    private SysUser currentUser;
    // 使用线程安全的set集合来当匹配池
    private static final Set<SysUser> matchPool = new CopyOnWriteArraySet<>();
    // 当前websocket连接的session对象
    private Session session;
    // 对战时长
    private int time;
    // 对战记录id
    private long battleRecordId;
    // 对战题目id
    private long problemId;
    // 计时线程
    private Thread timerThread;

    // 都是用set注入
    @Resource
    public void setSysUserService(SysUserService sysUserService) {
        WebsocketServer.sysUserService = sysUserService;
    }

    @Resource
    public void UserScoreService(UserScoreService userScoreService) {
        WebsocketServer.userScoreService = userScoreService;
    }

    @Resource
    public void ProblemService(ProblemService problemService) {
        WebsocketServer.problemService = problemService;
    }

    @Resource
    public void BattleRecordService(BattleRecordService battleRecordService) {
        WebsocketServer.battleRecordService = battleRecordService;
    }

    // 建立连接时调用的
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token) {
        JWT jwt = JWTUtil.parseToken(token);
        String userId = jwt.getPayload("userId").toString();
        currentUser = sysUserService.getById(Long.parseLong(userId));
        log.info("websocket 建立连接");
        sessions.put(currentUser.getUserId(), session);
    }

    // 关闭连接时调用
    @OnClose
    public void onClose() {
        log.info("websocket 关闭连接");
        sessions.remove(currentUser.getUserId());
    }

    // 接收到消息时调用
    @OnMessage
    public void onMessage(String message) {
        JSONObject jsonObject = JSONUtil.parseObj(message);
        String event = jsonObject.get("event", String.class);
        if ("start".equals(event)) {  // 开始匹配
            startMatch();
        } else if ("stop".equals(event)) {  // 取消匹配
            stopMatch();
        } else if ("end".equals(event)) {
            end(jsonObject);
        } else if ("defeat".equals(event)) {
            defeat(jsonObject);
        } else if ("info".equals(event)) {
            // info();
        }
    }

    // 开始匹配
    private void startMatch() {
        matchPool.add(currentUser);  // 加入匹配池

        while (matchPool.size() >= 2) {  // 如果池中有两个以上的用户，则可以匹配成功
            Iterator<SysUser> iterator = matchPool.iterator();
            // 取出两个用户
            SysUser userA = iterator.next();
            SysUser userB = iterator.next();
            // 这两个用户从池中删除
            matchPool.remove(userA);
            matchPool.remove(userB);

            // 获取随机题目
            Problem problem = getRandomProblem();
            this.problemId = problem.getId();

            // 创建对战记录
            BattleRecord battleRecord = new BattleRecord();
            battleRecord.setUserAId(userA.getUserId());
            battleRecord.setUserBId(userB.getUserId());
            battleRecord.setProblemId(problemId);
            battleRecordService.save(battleRecord);
            this.battleRecordId = battleRecord.getId();

            UserScoreVO userScoreA = userScoreService.getUserScoreByUserId(userA.getUserId());
            UserScoreVO userScoreB = userScoreService.getUserScoreByUserId(userB.getUserId());

            // 封装返回给前端的信息
            JSONObject respA = new JSONObject();
            JSONObject respB = new JSONObject();
            respA.set("opponent", userScoreA);  // 对手信息
            respA.set("status", "playing");  // 状态信息
            respA.set("problemId", problemId);  // 题目
            respB.set("opponent", userScoreB);
            respB.set("status", "playing");
            respB.set("problemId", problemId);

            // 将信息发送给前端
            send(userA.getUserId(), JSONUtil.toJsonStr(respB));
            send(userB.getUserId(), JSONUtil.toJsonStr(respA));

            timerThread = new Thread(() -> {
                while (time < 3600) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    time++;
                }
            });
            timerThread.start();
        }
    }

    // 取消匹配
    private void stopMatch() {
        // 从匹配池中删除
        matchPool.remove(currentUser);
    }

    // 结束游戏
    private void end(JSONObject jsonObject) {
        timerThread.interrupt();
        BattleRecord record = battleRecordService.getById(this.battleRecordId);
        record.setTime(DateUtil.timeToDateStr(this.time));
        record.setWiner(this.currentUser.getUserId());
        if (record.getUserAId().equals(currentUser.getUserId())) {
            record.setScoreA(50);
            record.setScoreB(-50);
            record.setCodeA(jsonObject.getStr("codeA"));
            record.setCodeB("");
        } else {
            record.setScoreA(-50);
            record.setScoreB(50);
            record.setCodeA("");
            record.setCodeB(jsonObject.getStr("codeB"));
        }
        battleRecordService.updateById(record);
        // 更新用户积分
        userScoreService.updateUserScoreByUserId(record.getUserAId(), record.getScoreA());
        userScoreService.updateUserScoreByUserId(record.getUserBId(), record.getScoreB());

        JSONObject resp = new JSONObject();
        resp.set("status", "end");
        resp.set("record", record);

        send(record.getUserAId(), JSONUtil.toJsonStr(resp));
        send(record.getUserBId(), JSONUtil.toJsonStr(resp));
    }

    // 投降游戏
    private void defeat(JSONObject jsonObject) {
        log.info("用户 {} 投降", this.currentUser.getUserName());
        timerThread.interrupt();
        BattleRecord record = battleRecordService.getById(this.battleRecordId);
        record.setTime(DateUtil.timeToDateStr(this.time));
        Long userAId = record.getUserAId();
        Long userBId = record.getUserBId();
        if (this.currentUser.getUserId().equals(userAId)) {
            record.setWiner(userBId);
            record.setScoreA(-50);
            record.setScoreB(50);
        } else {
            record.setWiner(userAId);
            record.setScoreA(50);
            record.setScoreB(-50);
        }
        battleRecordService.updateById(record);
        // 更新用户积分
        userScoreService.updateUserScoreByUserId(record.getUserAId(), record.getScoreA());
        userScoreService.updateUserScoreByUserId(record.getUserBId(), record.getScoreB());

        JSONObject resp = new JSONObject();
        resp.set("status", "end");
        resp.set("record", record);

        send(record.getUserAId(), JSONUtil.toJsonStr(resp));
        send(record.getUserBId(), JSONUtil.toJsonStr(resp));
    }
    // 随机获取题目
    private Problem getRandomProblem() {
        long count = problemService.count();
        int offset = new Random().nextInt((int) count);
        QueryWrapper<Problem> queryWrapper = new QueryWrapper<>();
        queryWrapper.last("limit " + offset + ", 1");
        return problemService.getOne(queryWrapper);
    }

    // 发送信息
    public void send(Long userId, String message) {
        // 用session对象发送信息，getAsyncRemote是线程安全的
        sessions.get(userId).getAsyncRemote().sendText(message);
    }

    // 连接出现错误时调用
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("连接失败，", error);
    }

    @Autowired
    public void setBattleRecordService(BattleRecordService battleRecordService) {
        this.battleRecordService = battleRecordService;
    }
}